from rest_framework.views import APIView
from rest_framework.generics import CreateAPIView
from .serializers import CartAddSerializer, CartListSerializer, CartSelectionSerializer
from rest_framework.response import Response
from meiduo_mall.utils import meiduo_json
from rest_framework import status
from goods.models import SKU
from . import constants
from django_redis import get_redis_connection


class CartView(APIView):
    def perform_authentication(self, request):
        # 在调用APIView时，这个方法会被调用，用于在执行视图方法前进行身份认证
        # 不需要在视图方法前执认证，则重写此方法，直接pass
        pass

    def post(self, request):
        # 1接收数据
        data = request.data

        # 2反序列化验证
        serializer = CartAddSerializer(data=data)
        serializer.is_valid(raise_exception=True)

        # 3序列化保存
        response = Response(serializer.validated_data)
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        # 判断用户是否登录，如果登录则写redis，如果未登录则写cookie
        # 如果未传认证信息则为匿名用户
        # 如果传递认证信息，则进行认证判断
        # 如果信息无效则抛异常
        # 如果信息有效，则表示当前登录的用户
        try:
            user = request.user
            # 身份信息正确，当前为登录状态
            # 1.获取redis连接
            redis_cli = get_redis_connection('cart')
            # 2.写hash：存商品编号、数量
            redis_cli.hincrby('cart_%d' % user.id, sku_id, count)
            # 3.写set：存商品编号，表示选中此商品
            if selected:
                redis_cli.sadd('cart_selected_%d' % user.id, sku_id)
        except:
            # 身份信息错误，当前为未登录状态
            # 4写cookie
            # 4.1读取cookie中现有的购物车数据
            if request.COOKIES.get('cart'):
                cart_dict = meiduo_json.loads(request.COOKIES.get('cart'))
            else:
                # 第一次加入购物车时，创建字典
                cart_dict = {}

            # 4.2添加新的商品信息
            # 4.2.1判断：是否存在此商品
            if sku_id in cart_dict:
                # 4.2.2如果存在则数量相加
                cart_dict[sku_id][count] += count
            else:
                # 4.2.3如果不存在则新增
                cart_dict[sku_id] = {
                    'count': count,
                    'selected': selected
                }

            # 4.3写cookie
            cart_str = meiduo_json.dumps(cart_dict)
            response.set_cookie('cart', cart_str, max_age=constants.CART_COOKIE_EXPIRES)

        return response

    def get(self, request):
        try:
            user = request.user
            # 从redis中读取购物车数据
            redis_client = get_redis_connection('cart')
            # 读取商品编号、数量
            skus_dict = redis_client.hgetall('cart_%d' % user.id)
            # 读取商品的选中状态
            sku_ids_selected = redis_client.smembers('cart_selected_%d' % user.id)
            # 整理数据的结构
            cart_dict = {}
            for sku_id, count in skus_dict.items():
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    'selected': sku_id in sku_ids_selected
                }
        except:
            # 1.读取cookie中的购物车数据
            cart_str = request.COOKIES.get('cart')
            if cart_str is None:
                return Response(status=status.HTTP_404_NOT_FOUND)
            cart_dict = meiduo_json.loads(cart_str)

        # 2.遍历商品编号，查询商品对象
        # cart_dict={sku_id:{'count':,'selected':}}
        skus = []
        for sku_id, sku_dict in cart_dict.items():
            # 查询商品对象
            sku = SKU.objects.get(pk=sku_id)
            # 指定数量
            sku.count = sku_dict.get('count')
            # 指定选中状态
            sku.selected = sku_dict.get('selected')
            skus.append(sku)

        # 3.使用序列化器输出
        serializer = CartListSerializer(skus, many=True)
        return Response(serializer.data)

    def put(self, request):
        # 接收，反序列化验证
        data = request.data
        serializer = CartAddSerializer(data=data)
        serializer.is_valid(raise_exception=True)

        # 获取数据
        response = Response(serializer.validated_data)
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        try:
            user = request.user
            redis_cli = get_redis_connection('cart')
            # 修改数量
            redis_cli.hset('cart_%d' % user.id, sku_id, count)
            # 修改选中状态
            if selected:
                redis_cli.sadd('cart_selected_%d' % user.id, sku_id)
            else:
                redis_cli.srem('cart_selected_%d' % user.id, sku_id)
        except:
            # 修改
            # 1.读cookie
            cart_str = request.COOKIES.get('cart')
            if cart_str is None:
                return Response(status=status.HTTP_404_NOT_FOUND)
            cart_dict = meiduo_json.loads(cart_str)

            # 2.修改：数量、选中状态
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }

            # 3.写cookie
            cart_str = meiduo_json.dumps(cart_dict)
            response.set_cookie('cart', cart_str, max_age=constants.CART_COOKIE_EXPIRES)

        return response

    def delete(self, request):
        # 1.接收数据
        data = request.data
        sku_id = data.get('sku_id')

        response = Response(status=status.HTTP_204_NO_CONTENT)

        try:
            user = request.user
            redis_cli = get_redis_connection('cart')
            # 删除hash
            redis_cli.hdel('cart_%d' % user.id, sku_id)
            # 删除选中状态
            redis_cli.srem('cart_selected_%d' % user.id, sku_id)
        except:
            # 2.从cookie中删除商品
            cart_str = request.COOKIES.get('cart')
            if cart_str is None:
                return Response(status=status.HTTP_404_NOT_FOUND)
            cart_dict = meiduo_json.loads(cart_str)
            del cart_dict[sku_id]

            # 3.响应：写cookie
            response.set_cookie('cart', meiduo_json.dumps(cart_dict), max_age=constants.CART_COOKIE_EXPIRES)

        return response


class CartSelectionView(APIView):
    def perform_authentication(self, request):
        pass

    def put(self, request):
        serializer = CartSelectionSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        selected = serializer.validated_data.get('selected')

        response = Response({"message": "ok"})

        try:
            user = request.user
            redis_cli = get_redis_connection('cart')
            # 读取hash中所有商品编号
            sku_ids = redis_cli.hkeys('cart_%d' % user.id)
            # 写入set中
            if selected:
                redis_cli.sadd('cart_selected_%d' % user.id, *sku_ids)
            else:
                redis_cli.delete('cart_selected_%d' % user.id)
        except:
            # 读cookie,改属性，写cookie
            cart_str = request.COOKIES.get('cart')
            if cart_str is None:
                return Response(status=status.HTTP_404_NOT_FOUND)
            cart_dict = meiduo_json.loads(cart_str)
            for sku_id, sku_dict in cart_dict.items():
                sku_dict['selected'] = selected
            response.set_cookie('cart', meiduo_json.dumps(cart_dict), max_age=constants.CART_COOKIE_EXPIRES)

        return response
